﻿using System;
using System.Collections.Generic;
using System.Linq;
using Granite.Data;
using LeadTracker.Models;
using System.ComponentModel.DataAnnotations;

namespace LeadTracker.DataAccess
{
	/// <summary>
	/// Data repository for Lead
	/// </summary>
	public class LeadRepository
	{
		readonly Connection m_Connection;
		readonly LeadCommentRepository m_LeadCommentRepository;

		/// <summary>
		/// Create a new data repository for Lead
		/// </summary>
		/// <param name="connection">Connection for making database/service calls</param>
		public LeadRepository(Connection connection)
		{
			m_Connection = connection;
			m_LeadCommentRepository = new LeadCommentRepository(connection);
		}

		/// <summary>
		/// Saves a new Lead. If the storage logic modifies values, the lead will be updated.
		/// Any comments attached will also be saved.
		/// </summary>
		/// <param name="lead">This is a lead containing the new values</param>
		public void Insert(LeadModel lead, int auditUserKey)
		{
			if (lead.HasErrors)
				throw new ValidationException("Lead has validation errors");

			using (var trans = m_Connection.BeginTransaction())
			{
				var cmd = new ProcedureCall("dbo.LeadInsertModel");
				cmd.Parameters.AutoPopulate("Insert", lead);
				cmd.Add("Audit_UserKey", auditUserKey);
				lead.Update(trans.ExecuteRow(cmd));


				foreach (var leadComment in lead.Comments)
				{
					leadComment.LeadKey = lead.LeadKey.Value;
					m_LeadCommentRepository.Insert(leadComment, auditUserKey, (Connection)trans);
				}

				trans.Commit();
			}

		}


		/// <summary>
		/// Updates an existing Lead. If the storage logic also modifies values, the lead will be updated.
		/// Any new comments will be added, any existing comments will be updated.
		/// </summary>
		/// <param name="lead">This is a lead containing the complete set of values</param>
		/// <remarks>Partial updates cannot be performed via this method.</remarks>
		public void Update(LeadModel lead, int auditUserKey)
		{
			if (lead.HasErrors)
				throw new ValidationException("Lead has validation errors");

			using (var trans = m_Connection.BeginTransaction())
			{
				var cmd = new ProcedureCall("dbo.LeadUpdateModel");
				cmd.Parameters.AutoPopulate("Update", lead);
				cmd.Add("Audit_UserKey", auditUserKey);
				lead.Update(trans.ExecuteRow(cmd));

				foreach (var leadComment in lead.Comments)
				{
					leadComment.LeadKey = lead.LeadKey.Value;

					if (leadComment.IsDirty)
					{
						if (leadComment.IsNew)
							m_LeadCommentRepository.Insert(leadComment, auditUserKey, (Connection)trans);
						else
							m_LeadCommentRepository.Update(leadComment, auditUserKey, (Connection)trans);
					}
					
				}
				trans.Commit();
			}
		}

		/// <summary>
		/// Overwrites any changes in a Lead with what is stored in the database. 
		/// </summary>
		/// <param name="lead">This is a lead to be filled.</param>
		/// <remarks>Only the fields labled with Action("Select") are needed.</remarks>
		public void Reload(LeadModel lead, int auditUserKey, Connection connection = null)
		{
			var cmd = new ProcedureCall("dbo.LeadSelecteModel");
			cmd.Parameters.AutoPopulate("Select", lead);
			cmd.Add("Audit_UserKey", auditUserKey);
			lead.Update((connection ?? m_Connection).ExecuteRow(cmd));
		}

		/// <summary>
		/// Deleted the indicated Lead.
		/// </summary>
		/// <param name="lead">Primary key of a Lead to be deleted.</param>
		public void DeleteLead(int? leadKey, int auditUserKey, Connection connection = null)
		{
			var cmd = new ProcedureCall("dbo.LeadDelete");
			cmd.Add("LeadKey", leadKey);
			cmd.Add("Audit_UserKey", auditUserKey);
			(connection ?? m_Connection).ExecuteNonQuery(cmd);
		}

		/// <summary>
		/// Loads the indicated Lead.
		/// </summary>
		/// <param name="lead">Primary key of a Lead to be loaded.</param>
		public LeadModel SelectLead(int? leadKey, int auditUserKey, Connection connection = null)
		{
			var cmd = new ProcedureCall("dbo.LeadSelectModel");
			cmd.Add("LeadKey", leadKey);
			cmd.Add("Audit_UserKey", auditUserKey);
			return new LeadModel((connection ?? m_Connection).ExecuteRow(cmd));
		}

		/// <summary>
		/// Selects all of the rows for use in a drop-down list
		/// </summary>
		/// <returns></returns>
		public List<LeadToken> SelectTokens(int auditUserKey, Connection connection = null)
		{
			var cmd = new ProcedureCall("dbo.LeadSelectAllTokens");
			cmd.Add("Audit_UserKey", auditUserKey);
			var table = (connection ?? m_Connection).ExecuteTable(cmd);
			var result = new List<LeadToken>();
			foreach (var row in table)
				result.Add(new LeadToken(row));
			return result;
		}



		public LeadCollectionPackage GetActiveLeads()
		{
			var tables = m_Connection.ExecuteTables(new ProcedureCall("dbo.ListActiveLeads"));

			return new LeadCollectionPackage(new LeadCollection(tables[0]), new EditorCollection(tables[1]));
		}


	}
}
